package top.cardone.data.jdbc.dao.impl;

import com.google.common.collect.Maps;
import lombok.NonNull;
import lombok.extern.log4j.Log4j2;
import org.springframework.util.CollectionUtils;
import top.cardone.context.ApplicationContextHolder;
import top.cardone.core.util.func.Func3;
import top.cardone.data.dao.SimpleDao;
import top.cardone.data.jdbc.support.NamedParameterJdbcOperationsSupport;

import java.util.List;
import java.util.Map;

/**
 * @author yao hai tao
 * @date 2015/8/26
 */
@Log4j2
public class SimpleDaoImpl extends CrudDaoImpl implements SimpleDao {
    @Override
    public <P> List<P> findList(@NonNull Class<P> mappedClass, Object findList) {
        Map<String, Object> findListMap = this.toMap(findList, "find");

        Map<String, Object> newFindListMap = Maps.newHashMap();

        for (Map.Entry<String, Object> findListEntry : findListMap.entrySet()) {
            newFindListMap.put("where_and_eq_" + findListEntry.getKey(), Boolean.TRUE.toString());
            newFindListMap.put("where_and_eq_" + findListEntry.getKey() + "_value", findListEntry.getValue());
        }

        return this.findListBySqlFileName(mappedClass, "find", newFindListMap);
    }

    @Override
    public <P> List<P> findListByFunc(@NonNull Class<P> mappedClass, @NonNull Func3 func, Object findList) {
        return (List<P>) func.func(mappedClass, this, findList);
    }

    @Override
    public <P> List<P> findListByFuncId(@NonNull Class<P> mappedClass, @NonNull String funcId, Object findList) {
        return this.findListByFunc(mappedClass, ApplicationContextHolder.getBean(Func3.class, funcId), findList);
    }

    @Override
    public <P> List<P> findList(@NonNull Class<P> mappedClass, @NonNull String sqlFilePath, Map<String, ?> findList) {
        String parseBeanId = this.getParseBeanId();
        String namedParameterJdbcOperationsBeanId = this.getNamedParameterJdbcOperationsBeanId();

        return (List<P>) ApplicationContextHolder.getBean(NamedParameterJdbcOperationsSupport.class).func(parseBeanId, sqlFilePath, findList, namedParameterJdbcOperationsBeanId, (sql, namedParameterJdbcOperations) -> namedParameterJdbcOperations.query(sql, findList, new org.springframework.jdbc.core.BeanPropertyRowMapper(mappedClass)));
    }

    @Override
    public <P> List<P> findListBySqlFileName(@NonNull Class<P> mappedClass, @NonNull String sqlFileName, Object findList) {
        return this.findList(mappedClass, this.getSqlFilePath(sqlFileName), this.toMap(findList, "find"));
    }

    @Override
    public <P> P findOne(@NonNull Class<P> mappedClass, Object findOne) {
        Map<String, Object> findOneMap = this.toMap(findOne, "find");

        Map<String, Object> newFindOneMap = Maps.newHashMap();

        for (Map.Entry<String, Object> findOneEntry : findOneMap.entrySet()) {
            newFindOneMap.put("where_and_eq_" + findOneEntry.getKey(), Boolean.TRUE.toString());
            newFindOneMap.put("where_and_eq_" + findOneEntry.getKey() + "_value", findOneEntry.getValue());
        }

        return this.findOneBySqlFileName(mappedClass, "find", newFindOneMap);
    }

    @Override
    public <P> P findOneByFunc(@NonNull Class<P> mappedClass, @NonNull Func3 func, Object findOne) {
        return (P) func.func(mappedClass, this, findOne);
    }

    @Override
    public <P> P findOneByFuncId(@NonNull Class<P> mappedClass, @NonNull String funcId, Object findOne) {
        return this.findOneByFunc(mappedClass, ApplicationContextHolder.getBean(Func3.class, funcId), findOne);
    }

    @Override
    public <P> P findOne(@NonNull Class<P> mappedClass, @NonNull String sqlFilePath, Map<String, ?> findOne) {
        List<P> findList = this.findList(mappedClass, sqlFilePath, findOne);

        if (CollectionUtils.isEmpty(findList)) {
            return null;
        }

        if (findList.size() > 1) {
            log.error("单条数据查询方法，查询出" + findList.size() + "条数据:sqlFilePath=" + sqlFilePath + ", findOne=" + findOne);
        }

        return findList.get(0);
    }

    @Override
    public <P> P findOneBySqlFileName(@NonNull Class<P> mappedClass, @NonNull String sqlFileName, Object findOne) {
        return this.findOne(mappedClass, this.getSqlFilePath(sqlFileName), this.toMap(findOne, "find"));
    }

    @Override
    public <R> List<R> readList(@NonNull Class<R> requiredType, Object readList) {
        Map<String, Object> readListMap = this.toMap(readList, "read");

        Map<String, Object> newReadListMap = Maps.newHashMap();

        for (Map.Entry<String, Object> readListEntry : readListMap.entrySet()) {
            if ("object_id".equals(readListEntry.getKey())) {
                continue;
            }

            newReadListMap.put("where_and_eq_" + readListEntry.getKey(), Boolean.TRUE.toString());
            newReadListMap.put("where_and_eq_" + readListEntry.getKey() + "_value", readListEntry.getValue());
        }

        newReadListMap.put("object_id", readListMap.get("object_id"));

        return this.readListBySqlFileName(requiredType, "read", newReadListMap);
    }

    @Override
    public <R> List<R> readListByFunc(@NonNull Class<R> requiredType, @NonNull Func3 func, Object readList) {
        return (List<R>) func.func(requiredType, this, readList);
    }

    @Override
    public <R> List<R> readListByFuncId(@NonNull Class<R> requiredType, @NonNull String funcId, Object readList) {
        return this.readListByFunc(requiredType, ApplicationContextHolder.getBean(Func3.class, funcId), readList);
    }

    @Override
    public <R> List<R> readList(@NonNull Class<R> requiredType, @NonNull String sqlFilePath, Map<String, ?> readList) {
        String parseBeanId = this.getParseBeanId();
        String namedParameterJdbcOperationsBeanId = this.getNamedParameterJdbcOperationsBeanId();

        return ApplicationContextHolder.getBean(NamedParameterJdbcOperationsSupport.class).func(parseBeanId, sqlFilePath, readList, namedParameterJdbcOperationsBeanId, (sql, namedParameterJdbcOperations) -> namedParameterJdbcOperations.queryForList(sql, readList, requiredType));
    }

    @Override
    public <R> List<R> readListBySqlFileName(@NonNull Class<R> requiredType, String sqlFileName, Object readList) {
        return this.readList(requiredType, this.getSqlFilePath(sqlFileName), this.toMap(readList, "read"));
    }

    @Override
    public <R> R readOne(@NonNull Class<R> requiredType, Object readOne) {
        Map<String, Object> readOneMap = this.toMap(readOne, "read");

        Map<String, Object> newReadOneMap = Maps.newHashMap();

        for (Map.Entry<String, Object> readOneEntry : readOneMap.entrySet()) {
            if ("object_id".equals(readOneEntry.getKey())) {
                continue;
            }

            newReadOneMap.put("where_and_eq_" + readOneEntry.getKey(), Boolean.TRUE.toString());
            newReadOneMap.put("where_and_eq_" + readOneEntry.getKey() + "_value", readOneEntry.getValue());
        }

        newReadOneMap.put("object_id", readOneMap.get("object_id"));

        return this.readOneBySqlFileName(requiredType, "read", newReadOneMap);
    }

    @Override
    public <R> R readOneByFunc(@NonNull Class<R> requiredType, @NonNull Func3 func, Object readOne) {
        return (R) func.func(requiredType, this, readOne);
    }

    @Override
    public <R> R readOneByFuncId(@NonNull Class<R> requiredType, @NonNull String funcId, Object readOne) {
        return this.readOneByFunc(requiredType, ApplicationContextHolder.getBean(Func3.class, funcId), readOne);
    }

    @Override
    public <R> R readOne(@NonNull Class<R> requiredType, @NonNull String sqlFilePath, Map<String, ?> readOne) {
        List<R> readList = this.readList(requiredType, sqlFilePath, readOne);

        if (CollectionUtils.isEmpty(readList)) {
            return null;
        }

        if (readList.size() > 1) {
            log.error("单条数据查询方法，查询出" + readList.size() + "条数据:sqlFilePath=" + sqlFilePath + ", readOne=" + readOne);
        }

        return readList.get(0);
    }

    @Override
    public <R> R readOneBySqlFileName(@NonNull Class<R> requiredType, String sqlFileName, Object readOne) {
        return this.readOne(requiredType, this.getSqlFilePath(sqlFileName), this.toMap(readOne, "read"));
    }
}